import moment from 'moment';
import _ from 'lodash';

/**
 * 定义分割符json，用于插入分割符
 * @param modelData
 * @param param
 */
const dividerJson = {
    row_style: '1,0,0',
    row_type: '5',
    display: '',
    data_list: '',
    data: '',
    cell: [
        {
            font_style: '0,0,0,0,1,-',
            cell_id: 'bl_header_split',
            name: '分隔符',
            width: '48',
            title: '',
            data_value: '',
        },
    ],
    row_child: [],
};
// 定义logo的各个id
const logoIdString = 'bl_header,bl_header_row_logo,bl_header_logo';
// 定义自定义标题的id
const subTitleIdString = 'bl_header,bl_header_row_user_title,bl_header_user_title';
// 定义底部图片的id
const bottomPicIdString = 'bl_tail,bl_tail_row_pic,bl_tail_pic';
// 定义底部底部栏id
const bottomTitleIdString = 'bl_tail,bl_tail_row_user_content,bl_tail_user_content';
/**
 * 根据提供的对象跟属性数组返回具体属性值
 * @param   {Object} modelData 目标数据对象
 * @param   {String} param 属性数组
 * @param   {Boolean} isExpression 是否求表达式的值
 */
const getParamsValue = (modelData, param, isExpression = false) => {
    let value;
    // 如果param有值，那么进行计算计算获取值，否则返回为空
    if (param) {
        value = modelData;
        const paramArray = param.split('.');
        for (let i = 0; i < paramArray.length; i += 1) {
            // 如果属性值是selfPrintTime，返回当前时间；如果当前不存在该属性，那么跳出循环，并且value设为空
            if (paramArray[i] === 'selfPrintTime') {
                value = moment().format('YYYY-MM-DD HH:mm:ss');
                break;
            } else if (value[paramArray[i]]) {
                value = value[paramArray[i]];
            } else if (value[paramArray[i]] === undefined && isExpression) { // 如果不存在该属性，并且是由表达式发出的话，那么返回为当前字符串本身，并且中止循环
                value = param;
                break;
            } else {
                value = '';
                break;
            }
        }
    } else {
        value = '';
    }
    return value;
};

/** 获取数据模型数据方法
  * {table_no}目标的field类似这样，我们需要把左右花括号去掉，还得去掉空格，这里用到正则
  * @param   {String} row_type 行类型，根据行类型返回不同的数据
  * @param   {String} parentParam 需要解析的模型字段
  * @param   {String} fieldParam 需要解析的模型字段
  * @param   {Object} modelData 需要解析的模型字段
  * @param   {Object} templateData 整个模型数据
  * @return  {String}
  */
const getModeData = (row_type, parentParam: string, fieldParam: string, modelData: any, templateData: any) => {
    /**
     * 父级结构体里的data_list跟data不可能是模板字符串，里面就是具体的层级
     * cell里面有具体的模板字符串，需要特别解析
     * cell里面data_value，有以下方式："{order.orderNo}"；"{dinner.dinnerType}/{dinner.dinnerNumber}"；{store.storeName}门店的{device.deviceId}#机器
     */
    // 目标返回值
    let fieldValue;
    // 花括号跟空格都得过滤，parent的因为获取的是值的集合，不允许有空格，采用不一样的正则
    const regStr = /([{}])/g;
    const regStrParent = /([{}\s])/g;
    // 父级模型key值获取
    const parent = parentParam ? parentParam.replace(regStrParent, '') : '';
    const field = fieldParam || '';
    // 如果父级模型属性属性存在并且长度大于0，那么先获取到父级的数组对象；根据层级赋予cell获取数据的对象参数modelDataParam
    const parentData = getParamsValue(modelData, parent);
    let modelDataParam = parent ? parentData : modelData;
    /**
     * 这里我们首先需要正则出几个变量
     * 然后获取到具体变量的值数组
     * 最后正则替换模板字符串，返回正确的字符串，替换的时候可以用之前的变量作为后面返回值对象的key值
     */
    // 定义正则
    const regReplace = /(?:\{([^{}]+)\})/g; // 这里全局匹配，匹配位于两个大括号之间的变量内容，里面应该包括点
    const regExpression = /(?:([^?:]*)\?([^?:]*):([^?:]*))/; // 这里是表达式解析语句，用捕获语句
    const regCondition = /(?:\s*(\S+)\s*(and|or|eq)?\s*(\S+)?\s*)/; // 这里是表达式解析语句，用捕获语句
    // 这里根据情况，看是否需要请求子字段，通过row_type进行判断，如果是列表，那么值返回父级的值
    if (row_type === '1') {
        fieldValue = parentData;
    } else {
        fieldValue = field.replace(regReplace, (match) => {
            let fieldItem = match.replace(regStr, '');
            let cellValue;
            // ===这里需要考虑普通字段跟表达式===
            if (fieldItem && typeof fieldItem === 'string' && fieldItem.indexOf('?') > -1) {
                let condition;
                const fragment = fieldItem.match(regExpression);
                // ===1.解析出表达式条件部分
                const expressionFragment = fragment[1];
                // 1) 这里不用正则，直接用字符串解析就行
                const expressionArray = expressionFragment.match(regCondition);
                // 2) 如果数组长度大于2，说明有两个前后的条件否则只有一个条件,表达式的条件目前不存在常量，所以在选择的时候，不用考虑常量返回原字符串的情况，如果返回原字符串，会影响判断条件
                if (expressionArray[2]) {
                    let firstConditon;
                    let lastConditon;
                    if (/^root\./.test(expressionArray[1])) {
                        firstConditon = getParamsValue(templateData, expressionArray[1].match(/^root\.([\w.]*)/)[1]);
                    } else {
                        firstConditon = getParamsValue(modelDataParam, expressionArray[1]);
                    }
                    if (/^root\./.test(expressionArray[3])) {
                        lastConditon = getParamsValue(templateData, expressionArray[3].match(/^root\.([\w.]*)/)[3]);
                    } else {
                        lastConditon = getParamsValue(modelDataParam, expressionArray[3]);
                    }
                    // 进入条件组装
                    switch (expressionArray[2]) {
                        case 'and':
                            condition = firstConditon && lastConditon;
                            break;
                        case 'or':
                            condition = firstConditon || lastConditon;
                            break;
                        case 'eq':
                            condition = firstConditon === lastConditon;
                            break;
                        default:
                            condition = false;
                            break;
                    }
                } else if (/^root\./.test(expressionArray[1])) {
                    condition = getParamsValue(templateData, expressionArray[1].match(/^root\.([\w.]*)/)[1]);
                } else {
                    condition = getParamsValue(modelDataParam, expressionArray[1]);
                }
                // 2) 根据conditon进行赋值,这里赋值的时候需要传入第三个参数，看是否为表达式取值，区别在于表达式取值的时候，如果模型数据没该字段，需要返回原字符串
                if (condition) {
                    if (/^root\./.test(fragment[2])) {
                        cellValue = getParamsValue(templateData, fragment[2].match(/^root\.([\w.]*)/)[1], true);
                    } else {
                        cellValue = getParamsValue(modelDataParam, fragment[2], true);
                    }
                } else if (/^root\./.test(fragment[3])) {
                    cellValue = getParamsValue(templateData, fragment[3].match(/^root\.([\w.]*)/)[1], true);
                } else {
                    cellValue = getParamsValue(modelDataParam, fragment[3], true);
                }
            } else {
                // 这里如果判断fieldItem是root.的字段，我们需要做参数更改
                if (/^root\./.test(fieldItem)) {
                    modelDataParam = templateData;
                    fieldItem = fieldItem.match(/^root\.([\w.]*)/)[1];
                }
                cellValue = getParamsValue(modelDataParam, fieldItem);
            }
            return cellValue;
        });
    }
    return fieldValue;
};
/**
 * 输入具体字符串计算实际字节长度
 * @param {string} item // 当前项
 * @param {string} cellSize // 字符大小
 * @return {number}
 */
const getStringByte = (item, cellSize) => {
    let itemByte = 0;
    // 定义正则
    const regDoubleByte = /[^\x00-\xff]/; // 双字节字符，包括汉字，这里我们可以替代汉字regHanzi跟regFullChar
    const regHalfChar = /[\u0000-\u00ff]/;
    const regDigitChar = /[0-9]/;
    // 先判断是否汉字，如果是汉字不管是否是全角、半角，计算为2
    if (regDoubleByte.test(item)) {
        itemByte = 2 * cellSize;
    } else if (regHalfChar.test(item)) {
        itemByte = 1 * cellSize;
    } else if (regDigitChar.test(item)) {
        itemByte = 1 * cellSize;
    }
    return itemByte;
};

/**
 * cell字符串二次处理，这里主要根据cell的宽度对cell里面的值做二次处理
 * cell值按cell宽度截取让后加换行符，剩余的用补位符补位，具体补位方式有左对齐、右对齐、居中对齐
 * 最后进行数组组装
 * 这里把标题跟cellvalue一起传进来即可
 * 如果具体的row的display为空，此时需要考虑title跟datavalue都为空的情形，此时需要cell的font_style的第六个字段补位
 * @param {string} cellValue // 字符串的值
 * @param {number} cellWidth // 定义的cell值的宽度
 * @param {string} coverChar // 补位符
 * @param {string} size // 字符大小
 * @param {string} align // 对齐方式
 * @return {string} //返回字符串
 */
const formatCellValue = (cellValue, cellWidth, coverChar, size, align) => {
    // 定义字符串存储数组，二维数组;分别是原始数组跟转换后数组
    // 这里的size还需要除去标题的长度，需要传入标题title
    const cellFormatArray = [];
    let formatString = '';
    let cellValueCharWidth = 0;
    let indexArray = 0;
    // 把原来的字符串转换为字符数组
    const cellValueArray = cellValue.split('');
    // 遍历计算长度，这里考虑font_style里第1个字段大小0：普通高度，1：2陪高度，2：3倍高度
    const cellSize = Number(size) + 1;
    // 具体内容的初始值应该是标题所占的字节数
    for (let i = 0; i < cellValueArray.length; i += 1) {
        // 先判断是否汉字，如果是汉字不管是否是全角、半角，计算为2
        const currentCharWidth = getStringByte(cellValueArray[i], cellSize);
        // 这里根据cellWidth跟cellValueCharWidth做比较，看是否加入到对应数组,向上取整减去1，这里不用向下取整的原因是恰好整除的被排除在外了
        // 这里长度计算的数组分组不能单靠字符，如果是汉字，剩余的空间虽然显示不了，但实际上空的位置也要考虑进去
        // 求值前看看，如果新的值发生变化，需要按前面行的整数倍来
        // 好比一个队伍的人，有胖的，有瘦的，我们安排每个列标准排16个人，那么我们需要记下当前排到第几列了，当前占用了多少人空间，
        // 当前列剩下的位置不够排下一个人的时候，需要往新列里排人。这里就是通过差值计算，不用除法。
        // 差值大于当前字符，加入现有的数组，等于0，加入现有数组并创建新数组，索引加一;小于当前字符，创建新数组，并加入新数组
        // 大于当前字符，当前总长度加当前字符长度。等于当前字符，加入当前字符长度;小于当前字符，当前长度为前面列长度再加当前字符长度。
        const diffNum = (indexArray + 1) * Number(cellWidth) - cellValueCharWidth;
        // 如果diffNum大于当前字符，那么当前字符加入到当前数组里，字符总宽度加上当前字符宽度
        if (diffNum > currentCharWidth) {
            if (!cellFormatArray[indexArray]) {
                cellFormatArray[indexArray] = [];
            }
            cellFormatArray[indexArray].push(cellValueArray[i]);
            cellValueCharWidth += currentCharWidth;
        } else if (diffNum === currentCharWidth) {
            if (!cellFormatArray[indexArray]) {
                cellFormatArray[indexArray] = [];
            }
            // 如果diffNum等于当前字符，那么当前字符加入到当前数组里，字符总宽度加上当前字符宽度，字符索引加1，另外创建新数组
            cellFormatArray[indexArray].push(cellValueArray[i]);
            cellValueCharWidth += currentCharWidth;
            // indexArray += 1;
            // if (!cellFormatArray[indexArray]) {
            //     cellFormatArray[indexArray] = [];
            // }
            // cellFormatArray[indexArray] = [];
        } else if (diffNum < currentCharWidth) {
            // 如果diffNum小于当前字符，字符索引加1，另外创建新数组，那么当前字符加入到新数组里，字符总宽度为前面列宽加上当前字符宽度，
            indexArray += 1;
            cellFormatArray[indexArray] = [];
            cellFormatArray[indexArray].push(cellValueArray[i]);
            cellValueCharWidth = indexArray * Number(cellWidth) + currentCharWidth;
        }
    }
    // 组装返回字符串，在cellFormatArray每一项后面增加换行符
    /**
     * &#10; — 换行Line feed,这里要使用\u000A字符，防止react转义
     * &#13; — 回车Carriage Return
     */
    cellFormatArray.forEach((item, index) => {
        // todo，这里假定标题长度小于cell，否则就没法显示，需要特别复杂的计算
        let tempString;
        tempString = item.join('');
        // 这里要对tempString进行一次空格替换的显示，默认的空格是不能正确显示出来的，需要&ensp；来替换需要&ensp;对应的&#8194;
        // 这个需要在拼装换行之前匹配，不然换行就被过滤了
        const regStrSpace = /\s/g;
        tempString = tempString.replace(regStrSpace, String.fromCharCode(8194));
        if (index < cellFormatArray.length - 1) {
            formatString += `${tempString}<br>`;
        } else {
            // 这里需要考虑增加补位符，这个需要根据对齐方式用switch完成
            // 对齐方式。0：居左对齐，1：居中对齐，2：居右对齐。这里其实需要有leftCoverString，rightCoverStr，还有tempString
            // 居左：tempString+fullCoverString；居右fullCoverString+tempString；居中：leftCoverString+tempString+rightCoverString（需要平分剩余，如果不均分，那么左少右多）
            // 只有当差值大于0的才进行补位,这里的递增的步调是字体大小
            // 如果当前是第一行，并且有标题，那么需要把标题的宽度去掉
            // 这里需要对item进行宽度字节计算
            // 在html中， " "空格是不占位的，我们需要用空格的unicode：U+0020
            // 空格需要&ensp;对应的&#8194;而不是&nbsp;对应的&#160;
            let itemBype = 0;
            for (let i = 0; i < item.length; i += 1) {
                itemBype += getStringByte(item[i], cellSize);
            }
            let coverWidth = 0;
            coverWidth = Math.floor((cellWidth - itemBype) / cellSize);
            let fullCoverString = '';
            // 我们检测到如果补位符是空格，那么就用String.fromCharCode(8194)
            const coverCharValue = regStrSpace.test(coverChar) ? String.fromCharCode(8194) : coverChar;
            for (let i = 0; i < coverWidth; i += 1) {
                fullCoverString += coverCharValue;
            }
            // 如果coverWidth是否被2整除，来计算左右string
            const leftCoverString = fullCoverString.substr(0, Math.floor(coverWidth / 2));
            const rightCoverString = fullCoverString.substr(Math.floor(coverWidth / 2));
            if (coverWidth > 0) {
                switch (align) {
                    case '0':
                        tempString = `${tempString}${fullCoverString}`;
                        break;
                    case '1':
                        tempString = `${leftCoverString}${tempString}${rightCoverString}`;
                        break;
                    case '2':
                        tempString = `${fullCoverString}${tempString}`;
                        break;
                    default:
                        tempString = `${tempString}${fullCoverString}`;
                        break;
                }
            }
            formatString += tempString;
        }
    });
    // 如果传入的cellValue为空，此时需要考虑补位,补位的时候也需要考虑到字体的大小
    if (!cellValue) {
        // 我们检测到如果补位符是空格，那么就用String.fromCharCode(8194)
        const regStrSpace = /\s/g;
        const coverCharValue = regStrSpace.test(coverChar) ? String.fromCharCode(8194) : coverChar;
        const coverWidth = Math.floor(cellWidth / cellSize);
        for (let i = 0; i < coverWidth; i += 1) {
            formatString += coverCharValue;
        }
    }
    return formatString;
};
/**
 * 这里根据传入的具体参数，遍历返回row_child
 * @param {string} rowId // 当前选中的blockId
 * @param {string} cellId // 当前选中的cellId
 * @param {object} field // 当前需要更新cellId下面的字段及值
 * @param {Array} rowChild
 * @return {Object} 有是否处理跟返回的rowChild
 */
const getRowChildData = (rowId, cellId, field, rowChild) => {
    let isCellEdited = false;
    const cacheChildJson = rowChild.map(rowsItem => {
        // 第一层过滤，看当前层是否有具体的cell
        if (rowsItem.row_id === rowId) {
            rowsItem.cell = rowsItem.cell.map((cellItem) => {
                // 6、匹配对应的cell,如果是选中的cell，那么给相应字段赋值
                if (cellItem.cell_id === cellId) {
                    cellItem = { ...cellItem, ...field };
                    isCellEdited = true;
                }
                return cellItem;
            });
        }
        const { row_child } = rowsItem;
        // 第二层过滤，考虑到第一层不满足的情况:isCellEdited为false，并且当前的row_type为1
        if (!isCellEdited && Array.isArray(row_child) && row_child.length > 0) {
            // 调用递归方法
            rowsItem.row_child = getRowChildData(rowId, cellId, field, row_child);
        }
        return rowsItem;
    });
    return cacheChildJson;
};

/**
 * 传入blockId, cellId, field，及billJson，最终返回新的billJson
 * 总体的原则是进入具体选中的cell，然后更改对应的值
 * @param {string} blockId // 当前选中的blockId
 * @param {string} rowId // 当前选中的blockId
 * @param {string} cellId // 当前选中的cellId
 * @param {object} field // 当前需要更新cellId下面的字段及值
 * @param {object} billJson //当前的billJson
 * @return {object} 返回新对象
 */
const getEditedBillJson = (blockId, rowId, cellId, field, billJson) => {
    // 定义是否完成cell操作, 如果找到具体的cell，并且处理完毕的话，那么就不再处理，否则继续往下走
    let isCellEdited = false;
    // 1、深拷贝billJson
    const billJsonData = _.cloneDeep(billJson);
    // 2、获取到选中的block
    billJsonData.block = billJsonData.block.map((blockItem) => {
        // 3、找到对应的block如果是选中的block，进一步处理，否则返回原来的元素
        if (blockItem.block_id === blockId) {
            // 4、遍历具体的rows
            blockItem.rows.map(rowsItem => {
                // 第一层过滤，处理普通的一层
                if (rowsItem.row_id === rowId) {
                    rowsItem.cell = rowsItem.cell.map((cellItem) => {
                        // 6、匹配对应的cell,如果是选中的cell，那么给相应字段赋值
                        if (cellItem.cell_id === cellId) {
                            cellItem = { ...cellItem, ...field };
                            isCellEdited = true;
                        }
                        return cellItem;
                    });
                }
                const { row_child } = rowsItem;
                // 第二层过滤，考虑到第一层不满足的情况:isCellEdited为false，并且当前的row_type为1
                if (!isCellEdited && row_child && Array.isArray(row_child) && row_child.length > 0) {
                    // 调用递归方法
                    rowsItem.row_child = getRowChildData(rowId, cellId, field, row_child);
                }
                return rowsItem;
            });
        }
        return blockItem;
    });
    return billJsonData;
};
/**
 * logo，自定义标题，底部图片，底部栏的修改，最终返回新的billJson
 * 总体的原则是进入具体选中的cell，然后更改对应的值
 * @param {string} type // 当前要操作的类型
 * @param {string} value // 当前修改data_value的值
 * @param {object} billJson //当前的billJson
 * @return {object} 返回新对象,这里包括原来的该值的旧值，用于改写判断
 */
const getEditedBillJsonForSpecial = (type, value, billJson) => {
    let oldDataValue;
    // 1、深拷贝billJson
    const billJsonData = _.cloneDeep(billJson);
    // 2、根据type找到对应的产生对应的id数组
    let typeids = [];
    switch (type) {
        case 'logo':
            typeids = logoIdString.split(',');
            break;
        case 'subTitle':
            typeids = subTitleIdString.split(',');
            break;
        case 'bottomPic':
            typeids = bottomPicIdString.split(',');
            break;
        case 'bottomTitle':
            typeids = bottomTitleIdString.split(',');
            break;
        default:
            typeids = logoIdString.split(',');
            break;
    }
    // 3、获取到选中的block
    billJsonData.block = billJsonData.block.map((blockItem) => {
        // 4、找到对应的block如果是选中的block，进一步处理，否则返回原来的元素
        if (blockItem.block_id === typeids[0]) {
            // 5、遍历具体的rows
            blockItem.rows = blockItem.rows.map(rowsItem => {
                if (rowsItem.row_id === typeids[1]) {
                    // 6、遍历具体row下面的cell
                    rowsItem.cell = rowsItem.cell.map((cellItem) => {
                        // 7、匹配对应的cell,如果是选中的cell，那么给相应字段赋值
                        if (cellItem.cell_id === typeids[2]) {
                            oldDataValue = cellItem.data_value;
                            cellItem = { ...cellItem, data_value: value };
                        }
                        return cellItem;
                    });
                }
                return rowsItem;
            });
        }
        return blockItem;
    });
    return { oldDataValue, billJsonData };
};
/**
 * 获取字段的值getCellValueForSpecial
 * 总体的原则是进入具体选中的cell，然后更改对应的值
 * @param {string} type // 当前要操作的类型
 * @param {object} billJson //当前的billJson
 * @return {string} 返回imageId
 */
const getCellValueForSpecial = (type, billJson) => {
    let cell_value = '';
    // 1、深拷贝billJson
    const billJsonData = _.cloneDeep(billJson);
    // 2、根据type找到对应的产生对应的id数组
    let typeids = [];
    switch (type) {
        case 'logo':
            typeids = logoIdString.split(',');
            break;
        case 'subTitle':
            typeids = subTitleIdString.split(',');
            break;
        case 'bottomPic':
            typeids = bottomPicIdString.split(',');
            break;
        case 'bottomTitle':
            typeids = bottomTitleIdString.split(',');
            break;
        default:
            typeids = logoIdString.split(',');
            break;
    }
    // 3、获取到选中的block
    if (billJsonData && billJsonData.block && Array.isArray(billJsonData.block)) {
        billJsonData.block.forEach((blockItem) => {
            // 4、找到对应的block如果是选中的block，进一步处理，否则返回原来的元素
            if (blockItem.block_id === typeids[0]) {
                // 5、遍历具体的rows
                blockItem.rows = blockItem.rows.forEach(rowsItem => {
                    if (rowsItem.row_id === typeids[1]) {
                        // 6、遍历具体row下面的cell
                        rowsItem.cell = rowsItem.cell.forEach((cellItem) => {
                            // 7、匹配对应的cell,如果是选中的cell，那么给相应字段赋值
                            if (cellItem.cell_id === typeids[2]) {
                                cell_value = cellItem.data_value;
                            }
                        });
                    }
                });
            }
        });
    }
    return cell_value;
};
/**
 * 获取字段是否已配置isCellChoiceForSpecial
 * 总体的原则是进入具体选中的cell，然后更改对应的值
 * @param {string} type // 当前要操作的类型
 * @param {object} billJson //当前的billJson
 * @return {boolean}} 返回imageId
 */
const isCellChoiceForSpecial = (type, billJson) => {
    let isCellChoice = false;
    // 1、深拷贝billJson
    const billJsonData = _.cloneDeep(billJson);
    // 2、根据type找到对应的产生对应的id数组
    let typeids = [];
    switch (type) {
        case 'logo':
            typeids = logoIdString.split(',');
            break;
        case 'subTitle':
            typeids = subTitleIdString.split(',');
            break;
        case 'bottomPic':
            typeids = bottomPicIdString.split(',');
            break;
        case 'bottomTitle':
            typeids = bottomTitleIdString.split(',');
            break;
        default:
            typeids = logoIdString.split(',');
            break;
    }
    // 3、获取到选中的block
    if (billJsonData && billJsonData.block && Array.isArray(billJsonData.block)) {
        billJsonData.block.forEach((blockItem) => {
            // 4、找到对应的block如果是选中的block，进一步处理，否则返回原来的元素
            if (blockItem.block_id === typeids[0]) {
                // 5、遍历具体的rows
                blockItem.rows = blockItem.rows.forEach(rowsItem => {
                    if (rowsItem.row_id === typeids[1]) {
                        // 6、遍历具体row下面的cell
                        rowsItem.cell = rowsItem.cell.forEach((cellItem) => {
                            // 7、匹配对应的cell,如果是选中的cell，那么给相应字段赋值
                            if (cellItem.cell_id === typeids[2]) {
                                isCellChoice = true;
                            }
                        });
                    }
                });
            }
        });
    }
    return isCellChoice;
};
/**
 * 传入blockId, rowItem, action，最终返回新的billJson
 * 总体的原则是进入具体选中的cell同时选中当前的row，然后更改对应的值
 * @param {string} blockId // 当前选中的blockId
 * @param {object} rowItem // 当前选中的row
 * @param {string} action // 当前具体操作
 * @param {object} billJson //当前的billJson
 * @return {object} 返回新对象
 */
const getActionedBillJson = (blockId, rowItemJson, action, billJson) => {
    // 这里顺便返回修改后的rowItemJson
    let cacheRowItemJson = _.cloneDeep(rowItemJson);
    // 定义空行的index，便于我们查找
    let dividerRowIndex;
    // 1、深拷贝billJson
    const billJsonData = _.cloneDeep(billJson);
    // 2、获取到选中的block
    billJsonData.block = billJsonData.block.map((blockItem) => {
        // cacheBlockItemRows的目的是为了改造当前的rows
        let cacheBlockItemRows = [];
        // 3、找到对应的block如果是选中的block，进一步处理，否则返回原来的元素
        if (blockItem.block_id === blockId) {
            // 4、遍历具体的rows,这里增加空行、减少空行、减少分割符都可以通过map完成
            blockItem.rows.map((rowsItem, indexRow) => {
                // 5、找到对于的rowsItem，进行相关修改
                if (rowsItem.row_id === rowItemJson.row_id) {
                    const rowStyleArray = rowsItem.row_style.split(',');
                    dividerRowIndex = indexRow + 1;
                    switch (action) {
                        case 'addBlack':
                            rowStyleArray[2] = String(Number(rowStyleArray[2]) + 1);
                            break;
                        case 'delBlack':
                            rowStyleArray[2] = String(Number(rowStyleArray[2]) - 1);
                            break;
                        default:
                            rowStyleArray[2] = String(Number(rowStyleArray[2]) + 1);
                            break;
                    }
                    // 重新组装
                    rowsItem.row_style = rowStyleArray.join(',');
                    cacheRowItemJson = rowsItem;
                }
                return rowsItem;
            });
            // 判断dividerRowIndex是否是分割符行，增加分割符; 通过foreach来实现赋值,这边需要考虑当前rows数组里没有dividerRowIndex这一项的情况
            if (action === 'addDivider') {
                // 这里需要对宽度进行调整
                const { width } = billJsonData;
                dividerJson.cell[0].width = String(width);
                if (blockItem.rows.length >= dividerRowIndex + 1 && blockItem.rows[dividerRowIndex].row_type !== '5') {
                    blockItem.rows.forEach((itemRow, indexRow) => {
                        // 如果当前的index值跟dividerRowIndex相同，那么先把分割符加进去，然后在加入其他元素
                        if (indexRow === dividerRowIndex) {
                            cacheBlockItemRows.push(dividerJson);
                        }
                        cacheBlockItemRows.push(itemRow);
                    });
                } else if (blockItem.rows.length < dividerRowIndex + 1) {
                    cacheBlockItemRows = _.cloneDeep(blockItem.rows);
                    cacheBlockItemRows.push(dividerJson);
                }
                blockItem.rows = cacheBlockItemRows;
                // 判断当前如果action是移除分割线，并且该行下面紧接着是一个分割符行的话，那么删除改行
            } else if (blockItem.rows.length >= dividerRowIndex + 1 && action === 'delDivider' && blockItem.rows[dividerRowIndex].row_type === '5') {
                blockItem.rows.forEach((itemRow, indexRow) => {
                    // 如果当前的index值跟dividerRowIndex相同，那么先把分割符加进去，然后在加入其他元素
                    if (indexRow !== dividerRowIndex) {
                        cacheBlockItemRows.push(itemRow);
                    }
                });
                blockItem.rows = cacheBlockItemRows;
            }
        }
        return blockItem;
    });
    return { billJsonData, cacheRowItemJson };
};
/**
 * 传入blockId, rowItem, billJson，最终返回该行下面是否有分割符
 * 总体的原则是进入具体选中的cell同时选中当前的row，然后更改对应的值
 * @param {string} blockId // 当前选中的blockId
 * @param {object} rowItem // 当前选中的row
 * @param {object} billJson //当前的billJson
 * @return {boolean} 返回新对象
 */
const isDividerRow = (blockId, rowItemJson, billJson) => {
    // 定义当前行下方是否有空行
    let isDivider;
    // 定义空行的index，便于我们查找
    let dividerRowIndex;
    // 1、深拷贝billJson
    const billJsonData = _.cloneDeep(billJson);
    // 2、获取到选中的block
    if (Array.isArray(billJsonData.block)) {
        billJsonData.block.forEach((blockItem) => {
            // 3、找到对应的block如果是选中的block，进一步处理，否则返回原来的元素
            if (blockItem.block_id === blockId) {
                // 4、遍历具体的rows,这里增加空行、减少空行、减少分割符都可以通过map完成
                blockItem.rows.forEach((rowsItem, indexRow) => {
                    // 5、找到对于的rowsItem，进行相关修改
                    if (rowsItem.row_id && rowsItem.row_id === rowItemJson.row_id) {
                        dividerRowIndex = indexRow + 1;
                    }
                });
                // 判断当前行下一行的row_type是否为5
                isDivider = dividerRowIndex && blockItem.rows.length >= dividerRowIndex + 1 ? blockItem.rows[dividerRowIndex].row_type === '5' : false;
            }
        });
    }
    return isDivider;
};
/**
 * 传入cell数组，最终返回宽度调整后的数组
 * @param {Array} cell // 当前传入的cell数组
 * @param {Array} cellTemplate // 当前传入的母版cell数组,如果为null，那么采用cell自己宽度调整
 * @param {Number} width // cell总宽度
 * @return {Array}
 */
const adjustCellWidth = (cell, cellTemplate, width) => {
    // 0. 复制原始cell
    const cacheCell = _.cloneDeep(cell);
    // 1. 获取当前cell总宽度
    let cellsWidth = 0;
    cacheCell.forEach(item => {
        // 找到模板里对于的cell
        const currentCellTemplate = cellTemplate ? _.find(cellTemplate, (itemCellTemplate) => itemCellTemplate.cell_id === item.cell_id) : item;
        cellsWidth += Number(currentCellTemplate.width);
    });
    // 2. 重新计算各cell的宽度, 如果不是第一项，那么向下取整；如果是第一项，就让总宽度减去前面占用的宽度
    let restWidth = 0;
    for (let i = cacheCell.length - 1; i > -1; i -= 1) {
        // 找到模板里对于的cell
        const currentCellTemplate = cellTemplate ? _.find(cellTemplate, (itemCellTemplate) => itemCellTemplate.cell_id === cell[i].cell_id) : cacheCell[i];
        if (i > 0) {
            const currentWidth = Math.floor(Number(currentCellTemplate.width) * width / cellsWidth);
            restWidth += currentWidth;
            cacheCell[i].width = String(currentWidth);
        } else {
            cacheCell[i].width = String(width - restWidth);
        }
    }
    return cacheCell;
};
/**
 * 传入rows数组，最终返回宽度调整后的数组
 * @param {Array} rows // 当前传入的rows数组
 * @param {Number} billWidth // 当前传入的母版cell数组,如果为null，那么采用cell自己宽度调整
 * @return {Array}
 */
// 对返回的默认模板数据进行二次处理，cell表格动态宽度; 如果存在row_child，那么进行递归
const adjustRowsWidth = (rows: any, billWidth: number) => {
    const cacheRows = _.cloneDeep(rows);
    cacheRows.map((itemRow: any) => {
        itemRow.cell = adjustCellWidth(itemRow.cell, null, billWidth);
        if (itemRow.row_child.length > 0) {
        // eslint-disable-next-line @typescript-eslint/camelcase
            itemRow.row_child = adjustRowsWidth(itemRow.row_child, billWidth);
        }
        return itemRow;
    });
    return cacheRows;
};
/**
 * 遍历获取cell的值
 * getRowChildCell
 * @param {string} rowId // 当前的row_id
 * @param {string} cellId // 当前的cell_id
 * @param {Array} parentRowIds // 当前的cell_id
 * @param {Array} levelIndex // 嵌套层级计数，主要是用于快速查找
 * @param {object} rowChild // 票据json
 * @return {object}
 */
let cacheSelectedRowItemForGetRowChildCell;
let cacheSelectedCellItemForGetRowChildCell;
const cacheSelectedRowListForGetRowChildCell = [];
const getRowChildCell = (rowId, cellId, parentRowIds, levelIndex, rowChild) => {
    // 返回对应的rowItem跟cellItem
    /**
     * 比如第三级：bl_dish_row_subdish2下面的bl_dish_sub_name2选择
     * rowId：bl_dish_row_subdish2
     * cellId：bl_dish_sub_name2
     * parentRowIds：['bl_dish_row_detail', 'bl_dish_row_subdish']
     * levelIndex：1 // 这里父级调用的时候是人为的增加了1，getAddCell里rowItem.row_child = getAddCell( rowId, cellId, parentRowIds, levelIndex + 1, rowItem.row_child, templateRowChild, cellIdList);
     * rowChild：[{row_id: 'bl_dish_row_subdish'...}, {row_id: 'bl_dish_row_topping'...}, {row_id: 'bl_dish_row_add'...}]
     */
    const rowChildData = _.cloneDeep(rowChild);
    const parentLevel = parentRowIds.length;
    // 1.1 如果当前levelIndex小于或者等于parentLevel，说明还在父级，没到当前的row，那么我们需要进行遍历
    if (levelIndex < parentLevel) { // 这里比较特殊，因为下面求值的时候levelIndex为level+1; 这里是1 < 2,要进入下一个递归
        // 1.2、forEach循环，找到对应的row_child
        rowChildData.forEach(rowItem => {
            if (rowItem.row_id === parentRowIds[levelIndex]) { // bl_dish_row_subdish === parentRowIds：['bl_dish_row_detail', 'bl_dish_row_subdish'] 中的第2个元素，即parentRowIds[1]
                getRowChildCell(rowId, cellId, parentRowIds, levelIndex + 1, rowItem.row_child);
            }
        });
    } else {
        // 2.1 如果当前levelIndex大于parentLevel，说明已经到了当前row，需要执行删除操作
        rowChildData.forEach((rowItem) => {
            // 4、找到对于的rowsItem，进行相关修改
            if (rowItem.row_id === rowId) {
                rowItem.cell.forEach(cellItem => {
                    if (cellItem.cell_id === cellId) {
                        cacheSelectedCellItemForGetRowChildCell = cellItem;
                    }
                });
                cacheSelectedRowItemForGetRowChildCell = rowItem;
            }
            cacheSelectedRowListForGetRowChildCell.push(rowItem.row_id);
        });
    }
};

/**
 * getAddCell方法主要是增加当前row_child里对应的cell，
 * 1.1 如果当前row_child不为空，而当前row也存在，当前cell也存在，那么需要按顺序插入当前cell
 * 1.2 如果当前row_child不为空，而当前row不存在，那么按顺序插入当前row，再插入当前cell
 * 1.3 如果当前的row_child为空，那么增加当前row，增加当前cell
 * 这里的row_child相当于block下面的rows选项,这里排序字段用不上
 * 由于递归，rowidList不好带过来，所以一起由getRowChildCell来计算
 * @param {string} rowId // 当前的row_id
 * @param {string} cellId // 当前的cell_id
 * @param {Array} parentRowIds // 当前的cell_id
 * @param {Number} levelIndex // 嵌套层级计数，主要是用于快速查找
 * @param {object} rowChild // 票据json
 * @param {Array} templateRowChild // 模板的rowChild
 * @param {Array} cellIdList // rowChild下面的cellIds排序
 * @param {Number} billWidth // 票据模板的宽度
 * @return {Array}
 * getAddCell( row_id, cell_id, parentRowIds, 0, row_child, cacheRowSelectedItem.row_child, cellIdList);
 */
const getAddCell = (rowId, cellId, parentRowIds, levelIndex, rowChild, templateRowChild, cellIdList, billWidth) => {
    let rowChildData = _.cloneDeep(rowChild);
    const rowChildDataForRow = _.cloneDeep(rowChild);
    let isRowForGetAddCell = false; // 当前是否存在该row
    const parentLevel = parentRowIds.length;
    const blockJsonForJson = _.cloneDeep(templateRowChild);
    // === 提取相关信息
    getRowChildCell(rowId, cellId, parentRowIds, levelIndex + 1, blockJsonForJson);
    // 数值解析
    /**
     * 比如第三级：bl_dish_row_subdish2下面的bl_dish_sub_name2选择
     * rowId：bl_dish_row_subdish2
     * cellId：bl_dish_sub_name2
     * parentRowIds：['bl_dish_row_detail', 'bl_dish_row_subdish']
     * levelIndex：0
     * rowChild：[{row_id: 'bl_dish_row_subdish'...}, {row_id: 'bl_dish_row_topping'...}, {row_id: 'bl_dish_row_add'...}]
     * templateRowChild：[{row_id: 'bl_dish_row_subdish'...}, {row_id: 'bl_dish_row_topping'...}, {row_id: 'bl_dish_row_add'...}]
     */
    // 1.1 如果当前levelIndex小于或者等于parentLevel，说明还在父级，没到当前的row，那么我们需要进行遍历
    // 这里的判断其实跟getRowChildCell的效果一样，只不过getRowChildCell里面的 if (levelIndex + 1 < parentLevel)
    // 按目前的情况，只需要递归一次就够了
    if (levelIndex < parentLevel - 1) {
        // 1.2、map循环，找到对应的row_child
        rowChildData.map(rowItem => {
            // 这里parentRowIds[levelIndex + 1])跟上面的getRowChildCell的if (rowItem.row_id === parentRowIds[levelIndex])
            // 效果一样，因为调用之前的levelIndex已经增加了1
            if (rowItem.row_id === parentRowIds[levelIndex + 1]) {
                // 这里递归给rowItem.row_child重新赋值
                // 这里的templateRowChild也需要递归
                const cacheTemplateRowChild = _.find(blockJsonForJson, (o) => o.row_id === rowItem.row_id);
                rowItem.row_child = getAddCell(rowId, cellId, parentRowIds, levelIndex + 1, rowItem.row_child, cacheTemplateRowChild.row_child, cellIdList, billWidth);
            }
            return rowItem;
        });
    } else {
        // 这里就进入叶子节点计算了，也是核心
    // 2.1 如果当前levelIndex大于parentLevel，说明已经到了当前row，需要执行删除操作
        rowChildData.map((rowItem) => {
            // 4、找到对于的rowsItem，进行相关修改；如果找到对应的row，那么isRowForGetAddCell为true，表示下面不用再增加该row了
            if (rowItem.row_id === rowId) {
                isRowForGetAddCell = true;
                // 2) 定义当前的cell
                const cacheCell = [];
                // 3）cellIdList遍历，优先考虑传入的cell_id，如果匹配，那么推入临时cell；否则在当前cell里遍历，如果有，加入; 这样保证顺序
                cellIdList.forEach(cellItemId => {
                    if (cellItemId === cellId) {
                        cacheCell.push(cacheSelectedCellItemForGetRowChildCell);
                    } else {
                        rowItem.cell.forEach(cellBillJsonItem => {
                            if (cellItemId === cellBillJsonItem.cell_id) {
                                cacheCell.push(cellBillJsonItem);
                            }
                        });
                    }
                });
                // todo 重新计算cell宽度
                rowItem.cell = adjustCellWidth(cacheCell, cacheSelectedRowItemForGetRowChildCell.cell, billWidth);
            }
            return rowItem;
        });
        // ****** 6、如果不存在当前row，拿rowList进行遍历，这里cacheSelectedRowListForGetRowChildCell是重新计算出来的，顶级的是参数传递过来的，因为递归不好计算当前row的idList序列
        if (!isRowForGetAddCell) {
            // 1) 定义当前的row
            const cacheRow = [];
            // 2）rowIdList遍历，优先考虑传入的row_id，如果匹配，那么推入临时row；否则在当前rows里遍历，如果有，加入; 这样保证顺序
            // 如果当前没有该信息，那么加入，否则不加入，这里有递归遍历的情况
            cacheSelectedRowListForGetRowChildCell.forEach(rowItemId => {
                if (rowItemId === rowId) {
                    const cacheRowChildDataFilter = cacheRow.filter(itemRow => itemRow.row_id === rowId);
                    // 加入之前，这里做二次校验，递归会导致多次加入
                    if (cacheRowChildDataFilter.length < 1) {
                        // 重新赋值cell
                        cacheSelectedRowItemForGetRowChildCell.cell = [cacheSelectedCellItemForGetRowChildCell];
                        cacheSelectedRowItemForGetRowChildCell.cell = adjustCellWidth(cacheSelectedRowItemForGetRowChildCell.cell, null, billWidth);
                        cacheRow.push(cacheSelectedRowItemForGetRowChildCell);
                    }
                } else {
                    rowChildDataForRow.forEach(rowBillJsonItem => {
                        if (rowItemId === rowBillJsonItem.row_id) {
                            const cacheRowChildDataFilter = cacheRow.filter(itemRow => itemRow.row_id === rowItemId);
                            // 加入之前，这里做二次校验，递归会导致多次加入
                            if (cacheRowChildDataFilter.length < 1) {
                                cacheRow.push(rowBillJsonItem);
                            }
                        }
                    });
                }
            });
            // 改写rows
            rowChildData = cacheRow;
        }
        // 过滤删除当前row
        rowChildData = rowChildData.filter(rowItemFilter => rowItemFilter.cell.length > 0);
    }
    // 返回计算后的rowChild
    return rowChildData;
};

/**
 * getRemoveCell方法主要是删除当前row_child里对应的cell，如果当前cell删除后，其父级的row下面的cell数组里没其他cell，那么该row需要删除； 如果父级row删除后，row_child下面没有其他row,那么row_child清空
 * 这里的row_child相当于block下面的rows选项,这里排序字段用不上
 * @param {string} rowId // 当前的row_id
 * @param {string} cellId // 当前的cell_id
 * @param {Array} parentRowIds // 当前的cell_id
 * @param {Array} levelIndex // 嵌套层级计数，主要是用于快速查找
 * @param {object} rowChild // 票据json
 * @param {Array} templateRowChild // 模板的rowChild
 * @param {Number} billWidth // 票据模板的宽度
 * @return {Array}
 */

const getRemoveCell = (rowId, cellId, parentRowIds, levelIndex, rowChild, templateRowChild, billWidth) => {
    // 数值解析
    /**
     * 比如第三级：bl_dish_row_subdish2下面的bl_dish_sub_name2选择
     * rowId：bl_dish_row_subdish2
     * cellId：bl_dish_sub_name2
     * parentRowIds：['bl_dish_row_detail', 'bl_dish_row_subdish']
     * levelIndex：0
     * rowChild：[{row_id: 'bl_dish_row_subdish'...}, {row_id: 'bl_dish_row_topping'...}, {row_id: 'bl_dish_row_add'...}]
     */
    const blockJsonForJson = _.cloneDeep(templateRowChild);
    // === 提取相关信息
    getRowChildCell(rowId, cellId, parentRowIds, levelIndex + 1, blockJsonForJson);
    let rowChildData = _.cloneDeep(rowChild);
    const parentLevel = parentRowIds.length;
    // 1.1 如果当前levelIndex小于或者等于parentLevel，说明还在父级，没到当前的row，那么我们需要进行遍历
    if (levelIndex < parentLevel - 1) { // 这里的目标只递归一次
        // 1.2、map循环，找到对应的row_child
        rowChildData.map(rowItem => {
            if (rowItem.row_id === parentRowIds[levelIndex + 1]) { // 这里的目标比对是+1的索引，跟getAddCell及getAddCell调用的getRowChildCell保持一致
                // 这里的templateRowChild也需要递归
                const cacheTemplateRowChild = _.find(blockJsonForJson, (o) => o.row_id === rowItem.row_id);
                rowItem.row_child = getRemoveCell(rowId, cellId, parentRowIds, levelIndex + 1, rowItem.row_child, cacheTemplateRowChild.row_child, billWidth);
            }
            return rowItem;
        });
    } else {
    // 2.1 如果当前levelIndex大于parentLevel，说明已经到了当前row，需要执行删除操作
    // 进入叶子节点，进行核心计算
        rowChildData.map((rowItem) => {
            // 4、找到对于的rowsItem，进行相关修改
            if (rowItem.row_id === rowId) {
                rowItem.cell = rowItem.cell.map(cellItem => {
                    if (cellItem.cell_id === cellId) {
                        return null;
                    }
                    return cellItem;
                });
                // 过滤删除当前cell
                rowItem.cell = rowItem.cell.filter(cellItemFilter => cellItemFilter);
                // todo 重新计算cell宽度
                rowItem.cell = adjustCellWidth(rowItem.cell, cacheSelectedRowItemForGetRowChildCell.cell, billWidth);
            }
            return rowItem;
        });
        // 过滤删除当前row
        rowChildData = rowChildData.filter(rowItemFilter => rowItemFilter.cell.length > 0);
    }
    // 返回计算后的rowChild
    return rowChildData;
};

/**
 * 选择具体cell后获取最新billJson的方法
 * getCellChooseBillJson
 * @param {boolean} action // 选择还是取消选择
 * @param {object} blockJson // 当前block的json
 * @param {string} row_id // 当前的row_id
 * @param {string} cell_id // 当前的cell_id
 * @param {Array} blockIdlist // block_id的数组
 * @param {Array} rowIdList // row_id的数组
 * @param {Array} cellIdList // cell_id的数组
 * @param {Array} parentRowIds // 父级rowid
 * @param {object} billJson // 票据json
 * @return {object}}
 *
 */
// eslint-disable-next-line max-len
const getCellChooseBillJson = (action, blockJson, row_id, cell_id, blockIdList, rowIdList, cellIdList, parentRowIds, billJson) => {
    // console.warn('getCellChooseBillJson888 begin', billJson);
    // 深拷贝具体的对象
    const billJsonData = _.cloneDeep(billJson);
    const blockJsonForCell = _.cloneDeep(blockJson);
    const blockJsonForRow = _.cloneDeep(blockJson);
    const blockJsonForBlock = _.cloneDeep(blockJson);
    // 票据模板宽度
    const billWidth = billJson.width;
    // === 提去相关信息
    // 1）提取当前cellItem
    let cacheCellSelectedItem;
    blockJsonForCell.rows.forEach(rowSelectedItem => {
        if (rowSelectedItem.row_id === row_id) {
            rowSelectedItem.cell.forEach(cellSelectedItem => {
                if (cellSelectedItem.cell_id === cell_id) {
                    cacheCellSelectedItem = cellSelectedItem;
                }
            });
        }
    });
    // 2）提取当前rowItem
    let cacheRowSelectedItem;
    blockJsonForRow.rows.forEach(rowSelectedItem => {
        // 如果parentRowIds的长度为0，那么就如此取值
        if (parentRowIds.length === 0) {
            if (rowSelectedItem.row_id === row_id) {
                cacheRowSelectedItem = rowSelectedItem;
            }
        } else if (parentRowIds.length > 0) {
            if (rowSelectedItem.row_id === parentRowIds[0]) {
                cacheRowSelectedItem = rowSelectedItem;
            }
        }
    });
    // 3）提取当前block,当前block不需要提取，传过来的就是

    // 判断如果是增加
    if (action) {
        // ======首先检测是否有block，然后检测row，如果没有添加，如果有，按顺序增加======
        // *****一、遍历billJsonData，正常逻辑推进，同时标识不存在的情况******
        // 1、定义block是否存在标识,后面会进行进一步处理，并且block开始遍历
        let isBlock = false;
        billJsonData.block = billJsonData.block.map((blockItem) => {
            // 2、找到对应的block如果是选中的block，进一步处理，否则返回原来的元素
            if (blockItem.block_id === blockJson.block_id) {
                isBlock = true;
                // 3、定义row是否存在标识，并且遍历row
                let isRow = false;
                blockItem.rows = blockItem.rows.map((rowsItem) => {
                    const { row_child } = rowsItem;
                    // 4、找到对于的rowsItem，给isRow打标，这里其实已经包含了row_type为1，但是顶层的情况
                    if (parentRowIds.length < 1) {
                        if (rowsItem.row_id === row_id) {
                            isRow = true;
                            // 5、根据cell顺序，加入对应的cell，这里需要遍历cellIdList
                            // 2) 定义当前的cell
                            const cacheCell = [];
                            // 3）cellIdList遍历，优先考虑传入的cell_id，如果匹配，那么推入临时cell；否则在当前cell里遍历，如果有，加入; 这样保证顺序
                            cellIdList.forEach(cellId => {
                                if (cellId === cell_id) {
                                    cacheCell.push(cacheCellSelectedItem);
                                } else {
                                    rowsItem.cell.forEach(cellBillJsonItem => {
                                        if (cellId === cellBillJsonItem.cell_id) {
                                            cacheCell.push(cellBillJsonItem);
                                        }
                                    });
                                }
                            });
                            // todo 重新计算cell宽度
                            rowsItem.cell = adjustCellWidth(cacheCell, cacheRowSelectedItem.cell, billWidth);
                        }
                    } else if (parentRowIds.length > 0) {
                        // 一旦进入row_child，就不能对顶层构成影响
                        isRow = true;
                        if (rowsItem.row_id === parentRowIds[0]) {
                            // 如果父id的数组第一位等于当前rowid,那么递归求值，下面就只影响到具体row_child下面的内容，对顶层已经无影响
                            rowsItem.row_child = getAddCell(row_id, cell_id, parentRowIds, 0, row_child, cacheRowSelectedItem.row_child, cellIdList, billWidth);
                        }
                    }
                    return rowsItem;
                });
                // ****** 6、如果不存在当前row，拿rowList进行遍历
                if (!isRow) {
                    // 1) 定义当前的row
                    const cacheRow = [];
                    // 2）rowIdList遍历，优先考虑传入的row_id，如果匹配，那么推入临时row；否则在当前rows里遍历，如果有，加入; 这样保证顺序
                    rowIdList.forEach(rowId => {
                        if (rowId === row_id) {
                            // 重新赋值cell
                            cacheRowSelectedItem.cell = [cacheCellSelectedItem];
                            cacheRowSelectedItem.cell = adjustCellWidth(cacheRowSelectedItem.cell, cacheRowSelectedItem.cell, billWidth);
                            cacheRow.push(cacheRowSelectedItem);
                        } else {
                            blockItem.rows.forEach(rowBillJsonItem => {
                                if (rowId === rowBillJsonItem.row_id) {
                                    cacheRow.push(rowBillJsonItem);
                                }
                            });
                        }
                    });
                    // 改写rows
                    blockItem.rows = cacheRow;
                }
            }
            return blockItem;
        });
        // ****** 7、如果不存在当前block，拿blockList进行遍历
        if (!isBlock) {
            // 1) 定义当前的block
            const cacheBlock = [];
            // 2）blockIdList遍历，优先考虑传入选中的block_id，如果匹配，那么推入临时block；否则在当前block里遍历，如果有，加入; 这样保证顺序
            blockIdList.forEach(blockId => {
                if (blockId === blockJsonForBlock.block_id) {
                    // 重新赋值cell
                    cacheRowSelectedItem.cell = [cacheCellSelectedItem];
                    // 抽取blockJsonForBlock的最后一个row，如果是分割符就保留
                    const cacheBlackDividerRow = blockJsonForBlock.rows.filter(rowItemForBlock => rowItemForBlock.row_type === '5');
                    // 重新赋值rows,这里需要考虑保留原来的分割符的row
                    blockJsonForBlock.rows = [cacheRowSelectedItem];
                    // 如果有空行或者分割符，那么追加到后面
                    if (cacheBlackDividerRow.length > 0) {
                        blockJsonForBlock.rows.push(cacheBlackDividerRow[0]);
                    }
                    cacheBlock.push(blockJsonForBlock);
                } else {
                    billJsonData.block.forEach(blockBillJsonItem => {
                        if (blockId === blockBillJsonItem.block_id) {
                            cacheBlock.push(blockBillJsonItem);
                        }
                    });
                }
            });
            // 改写block
            billJsonData.block = cacheBlock;
        }

    } else {
        // ======如果是取消选择，那么对应的block，row，cell肯定存在======
        // 取消选择不用考虑rowIdList的问题，只需要做好过滤即可
        // 1. 遍历billJsonData
        billJsonData.block = billJsonData.block.map((blockItem) => {
            // 2、找到对应的block如果是选中的block，进一步处理，否则返回原来的元素
            if (blockItem.block_id === blockJson.block_id) {
                // 3、遍历具体的rows,找到对应的row
                blockItem.rows = blockItem.rows.map((rowsItem) => {
                    const { row_child } = rowsItem;
                    // 4、找到对于的rowsItem，进行相关修改，这里需要考虑parentRowIds数组是否大于0的情况，考虑是否顶层
                    // 4.1 顶层对象
                    if (parentRowIds.length < 1) {
                        if (rowsItem.row_id === row_id) {
                            rowsItem.cell = rowsItem.cell.map(cellItem => {
                                if (cellItem.cell_id === cell_id) {
                                    return null;
                                }
                                return cellItem;
                            });
                            // 过滤删除当前cell
                            rowsItem.cell = rowsItem.cell.filter(cellItemFilter => cellItemFilter);
                            // todo 重新计算cell宽度
                            rowsItem.cell = adjustCellWidth(rowsItem.cell, null, billWidth);
                        }
                    } else if (parentRowIds.length > 0) {
                        // 4.2 row_type对象, 我们将对row_child进行重新计算，此时已经不影响顶层row了
                        if (row_child && Array.isArray(row_child) && rowsItem.row_id === parentRowIds[0]) {
                            rowsItem.row_child = getRemoveCell(row_id, cell_id, parentRowIds, 0, row_child, cacheRowSelectedItem.row_child, billWidth);
                        }
                    }
                    return rowsItem;
                });
                // 过滤删除当前row
                blockItem.rows = blockItem.rows.filter(rowItemFilter => rowItemFilter.cell.length > 0);
            }
            return blockItem;
        });
        // 过滤当前block，要求当前block里面的有效rows长度大于0; 这里需要层层过滤；这里主要是删除掉cell数组长度为0的row跟row长度为0的block
        billJsonData.block = billJsonData.block.filter(blockItemFilter => {
            // 这里需要深拷贝，不然会在没有取消完其他选项的时候过滤掉空格跟分割符
            const blockItemFilterData = _.cloneDeep(blockItemFilter);
            // 获取有效rows, 这里考虑分割线的情况
            blockItemFilterData.rows = blockItemFilterData.rows.filter(blockRowsFilter => {
                // 这里还需要考虑分割线的情况，需要增加row_type为5的情况
                blockRowsFilter.cell = blockRowsFilter.cell.filter(blockCellFilter => blockRowsFilter.row_type === '5' || blockCellFilter.title || blockCellFilter.data_value);
                if (blockRowsFilter.cell.length > 0) {
                    return true;
                } else {
                    return false;
                }
            });
            // 计算分割线rows
            const dividerBockItemFilter = blockItemFilterData.rows.filter(blockRowsFilter => blockRowsFilter.row_type === '5');
            // 这里需要考虑到如果剩的row的长度跟分割线的长度一样的话，那么就不显示；返回为true的情况是当前row的长度大于分割线的长度
            if (blockItemFilterData.rows.length > dividerBockItemFilter.length) {
                return true;
            } else {
                return false;
            }
        });
    }
    // console.warn('getCellChooseBillJson888 return', billJsonData);
    return billJsonData;
};

/**
 * 获取key，可以传入相应的id，动态的产生key
 * @param   {string} id 具体id
 * @return  {string} 返回参数：生成的可以
 */
const getKey = id => `${id || ''}_${new Date().getTime()}`;
// 获取session
const getSession = name => sessionStorage.getItem(name);

export {
    getParamsValue,
    getModeData,
    formatCellValue,
    getEditedBillJson,
    getEditedBillJsonForSpecial,
    getCellValueForSpecial,
    isCellChoiceForSpecial,
    getActionedBillJson,
    getCellChooseBillJson,
    isDividerRow,
    getKey,
    getSession,
    adjustCellWidth,
    adjustRowsWidth,
};
